package com.myzl.coal.client.parse;

import com.alibaba.fastjson2.JSONArray;
import com.google.common.primitives.Bytes;
import com.myzl.coal.client.CacheData;
import com.myzl.coal.client.config.LocalCommunicationParse;
import com.myzl.coal.domain.RealTimeData;
import com.myzl.coal.mongo.component.MongoComponent;
import com.myzl.coal.mongo.domain.HistoryData;
import com.myzl.coal.service.IRealTimeDataService;
import com.myzl.coal.service.impl.DemoService;
import com.myzl.common.constant.DeviceConstants;
import com.myzl.common.core.domain.entity.SysDictData;
import com.myzl.common.enums.DemoEnum;
import com.myzl.common.enums.DeviceCommandEnum;
import com.myzl.common.enums.DeviceTypeEnum;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import static com.myzl.coal.utils.HexUtil.*;

/**
 * @author 91941
 */
@Component
@Slf4j(topic = "admin-collect")
@LocalCommunicationParse(deviceType = DeviceConstants.BACK_LIDAR)
@Data
public class LidarParse extends CommonParseService {

    Map<Integer, Integer> countMap = new ConcurrentHashMap<>(16);
    Map<Integer, byte[]> rangesMap = new ConcurrentHashMap<>(16);
    Map<Integer, LocalDateTime> timeMap = new ConcurrentHashMap<>(16);

    private final IRealTimeDataService realTimeDataService;
    private final CacheData cacheData;
    private final MongoComponent mongoComponent;
    private final DemoService demoService;

    /**
     * 客户端发消息会触发
     */
    @Override
    public void channelRead(Object msg, Integer deviceId) {
        try {
            byte[] dataBytes = (byte[]) msg;
//            String dataStr = bytesToHexString(dataBytes);
//            log.info("Lidar receive Data:{}", dataStr);
            String signature = getString(dataBytes, 0, 2);
            String reserved1 = getString(dataBytes, 2, 2);
            int block = getInt(dataBytes, 4, 1);
            int flags = getInt(dataBytes, 5, 1);
            byte[] countBytes = getBytes(dataBytes, 6, 2);
            Bytes.reverse(countBytes);
            int count = toInt(countBytes);
            long timestamp = getLong(dataBytes, 8, 4);
            String checksum = getString(dataBytes, 12, 2);
            String reserved2 = getString(dataBytes, 14, 2);
            byte[] ranges = getBytes(dataBytes, 16, count * 2);
            byte[] intensities = getBytes(dataBytes, 16 + count * 2, count);


            if (timeMap.containsKey(deviceId)) {
                LocalDateTime dateTime = timeMap.get(deviceId);
                if (dateTime.plusMinutes(1).isAfter(LocalDateTime.now())) {
                    return;
                }
            }

            Integer blockCount = countMap.getOrDefault(deviceId, 0);
            if (!Objects.equals(blockCount, block)) {
                countMap.put(deviceId, 0);
                rangesMap.put(deviceId, new byte[0]);
                return;
            }
            countMap.put(deviceId, ++blockCount);
            byte[] totalBytes = rangesMap.getOrDefault(deviceId, new byte[0]);
            byte[] concat = Bytes.concat(totalBytes, ranges);
            rangesMap.put(deviceId, concat);

            if (blockCount <= 7) {
                return;
            }

            /**获取拼接data解析*/
            byte[] bytes = rangesMap.get(deviceId);
            log.info("Lidar receive Data:{}", bytesToHexString(bytes));

            /**获取初始数据*/
            double offset = 2;
            double totalH = cacheData.getRealTimeData(deviceId, DeviceCommandEnum.HIGH.getCode()) + offset;
            double halfX = BigDecimal.valueOf(cacheData.getRealTimeData(deviceId, DeviceCommandEnum.WIDTH.getCode())).divide(BigDecimal.valueOf(2), 2, RoundingMode.HALF_UP).doubleValue();


            List<Double> vLists = new ArrayList<>();
            List<Double> xLists = new ArrayList<>();
            List<Double> yLists = new ArrayList<>();
            List<HistoryData> historyDataList = new ArrayList<>();
            List<RealTimeData> realTimeDataList = new ArrayList<>();
            double start = 0;
            double end = 0;
            for (int i = 0, j = 0; i < bytes.length; i += 2, j++) {
                byte[] data = getBytes(bytes, i, 2);
                Bytes.reverse(data);
                int value = toInt(data);
                BigDecimal vBigDecimal = BigDecimal.valueOf(value).multiply(BigDecimal.valueOf(0.2));
                double v = vBigDecimal.setScale(2, RoundingMode.HALF_UP).doubleValue();
                BigDecimal oneDegrees = BigDecimal.valueOf(270).divide(BigDecimal.valueOf(count * 8), 5, RoundingMode.HALF_UP);
                double degrees = oneDegrees.multiply(BigDecimal.valueOf(j)).add(BigDecimal.valueOf(45)).setScale(5, RoundingMode.HALF_UP).doubleValue();
                double radians = Math.toRadians(degrees);
                double sin = Math.sin(radians);
                double cos = Math.cos(radians);
                double x = -vBigDecimal.multiply(BigDecimal.valueOf(sin)).setScale(2, RoundingMode.HALF_UP).doubleValue();
                double y = vBigDecimal.multiply(BigDecimal.valueOf(cos)).setScale(2, RoundingMode.HALF_UP).doubleValue();

                if ((Math.abs(x) < halfX || v == 0) && degrees > 90 && degrees < 270) {
                    double realX = 0;
                    double realY = 0;

                    if (start == 0) {
                        if (v == 0) {
                            continue;
                        }
                        end = 180 - degrees + 180;
                        start = degrees;
                    }

                    if (degrees >= start && degrees <= end) {

                        if (v != 0) {
                            realX = BigDecimal.valueOf(x).add(BigDecimal.valueOf(halfX)).setScale(1, RoundingMode.HALF_UP).doubleValue();
                            realY = BigDecimal.valueOf(totalH).add(BigDecimal.valueOf(y)).setScale(1, RoundingMode.HALF_UP).doubleValue();
                            //y不出现负数
                            realY = realY > 0 ? realY : 0.1;
                        }
                        log.debug("点位:{},角度:{} 度,原始值:{}cm,x:{},y:{},realX:{}cm,realY:{}cm", j, degrees, v, x, y, realX, realY);

                        vLists.add(v);
                        xLists.add(realX);
                        yLists.add(realY);
                    }

                } else {
                    double realX = BigDecimal.valueOf(x).add(BigDecimal.valueOf(halfX)).setScale(2, RoundingMode.HALF_UP).doubleValue();
                    double realY = BigDecimal.valueOf(totalH).add(BigDecimal.valueOf(y)).setScale(2, RoundingMode.HALF_UP).doubleValue();
                    log.debug("----点位:{},角度:{} 度,原始值:{}cm,x:{},y:{},realX:{}cm,realY:{}cm", j, degrees, v, x, y, realX, realY);
                }
            }

            storage(deviceId, vLists, xLists, yLists, historyDataList, realTimeDataList);
            mongoComponent.saveBatch(historyDataList, HistoryData.class);
            realTimeDataService.updateRealTimeDataByType(realTimeDataList);

            timeMap.put(deviceId, LocalDateTime.now());

        } catch (Exception e) {
            log.info("receive data error:", e);
        }
    }

    public void storage(Integer deviceId, List<Double> vLists, List<Double> xLists, List<Double> yLists, List<HistoryData> historyDataList, List<RealTimeData> realTimeDataList) {

        int size = yLists.size();
        int intValue = BigDecimal.valueOf(size + 1).multiply(BigDecimal.valueOf(0.3)).setScale(0, RoundingMode.HALF_UP).intValue();

        for (int i = 1; i < size - 1; i++) {
            double currentY = yLists.get(i);
            if (demoService.getRedisPoliceFlag(DemoEnum.coalHigh)) {
                yLists.set(i, currentY + 30);
            }
            if (demoService.getRedisPoliceFlag(DemoEnum.coalDistribution)) {
                if (i < intValue) {
                    yLists.set(i, currentY + 15);
                }
            }
        }

//        for (int i = 1; i < yLists.size() - 1; i++) {
//
//            double currentY = yLists.get(i);
//            double currentX = xLists.get(i);
//
//            setEle(yLists, i, currentY);
//            setEle(xLists, i, currentX);
//        }


            List<Double> left = yLists.subList(0, intValue);
            List<Double> middle = yLists.subList(intValue, size - intValue);
            List<Double> right = yLists.subList(size - intValue, size);

            double totalAvg = yLists.stream().mapToDouble(Double::valueOf).average().getAsDouble();
            double leftAvg = left.stream().mapToDouble(Double::valueOf).average().getAsDouble();
            double middleAvg = middle.stream().mapToDouble(Double::valueOf).average().getAsDouble();
            double rightAvg = right.stream().mapToDouble(Double::valueOf).average().getAsDouble();

            List<String> storage = new ArrayList<>();
            storage.add(new JSONArray(vLists).toString());
            storage.add(new JSONArray(xLists).toString());
            storage.add(new JSONArray(yLists).toString());

            storage.add(Double.toString(BigDecimal.valueOf(totalAvg).setScale(2, RoundingMode.HALF_UP).doubleValue()));
            storage.add(Double.toString(BigDecimal.valueOf(leftAvg).setScale(2, RoundingMode.HALF_UP).doubleValue()));
            storage.add(Double.toString(BigDecimal.valueOf(middleAvg).setScale(2, RoundingMode.HALF_UP).doubleValue()));
            storage.add(Double.toString(BigDecimal.valueOf(rightAvg).setScale(2, RoundingMode.HALF_UP).doubleValue()));

            for (int i = 0; i < storage.size(); i++) {
                Integer type = 6000 + i;
                Map<String, SysDictData> dictMap = CacheData.getStringSysDictDataMap();
                SysDictData sysDictData = dictMap.get(type.toString());
                RealTimeData realTimeData = new RealTimeData();
                HistoryData historyData = new HistoryData();
                realTimeData.setDeviceId(deviceId);
                realTimeData.setDictValue(type);
                realTimeData.setDataUnit(sysDictData.getValueUnit());
                realTimeData.setDeviceType(DeviceTypeEnum.LIDAR.getCode());
                realTimeData.setDataValue(storage.get(i));

                BeanUtils.copyProperties(realTimeData, historyData);
                historyData.initBasic();
                realTimeDataList.add(realTimeData);
                historyDataList.add(historyData);
            }

        }

        public static void setEle (List < Double > yLists,int i, double currentY){
            if (currentY == 0) {
                double pre = getPre(yLists, i);
                double next = getNext(yLists, i);
                if (pre != 0 && next != 0) {
                    double v = BigDecimal.valueOf(pre).add(BigDecimal.valueOf(next)).divide(BigDecimal.valueOf(2)).setScale(2, RoundingMode.HALF_UP).doubleValue();
                    yLists.set(i, v);
                }
            }
        }

        public static double getPre (List < Double > yLists,int i){
            double pre = 0;
            while (i > 1) {
                pre = yLists.get(--i);
                if (pre > 0) {
                    break;
                }
            }
            return pre;
        }

        public static double getNext (List < Double > yLists,int i){
            double next = 0;
            while (i < (yLists.size() - 1)) {
                next = yLists.get(++i);
                if (next > 0) {
                    break;
                }
            }
            return next;
        }


    }
